package service

import (
	"file_explorer/common/consts"
	"file_explorer/common/log"
	"file_explorer/common/util"
	"file_explorer/model"
	"os"

	"github.com/shirou/gopsutil/disk"
)

// 查询子文件 (包含目录)
func QueryFiles(dir string, option *model.QueryOption) ([]*model.FileInfo, error) {
	if option == nil {
		option = model.GetDefaultOption()
	}

	dir = util.FormatPath(dir)

	if util.IsOsWindows() && dir == "/" {
		return getWindowsDrivers()
	}

	// 读取目录
	rawFileInfos, err := readDir(dir)
	if err != nil {
		log.Error("query files failed, dir=%v, err=%v", dir, err)
		return nil, err
	}

	// 填充扩展信息
	rawFileInfos = fillFileMetadata(dir, rawFileInfos, option)

	// 过滤
	fileInfos := make([]*model.FileInfo, 0, len(rawFileInfos))
	for _, fileInfo := range rawFileInfos {
		if option.Filter(fileInfo) {
			fileInfos = append(fileInfos, fileInfo)
		}
	}

	log.Debug("query files success, dir=%v, fileNum=%v", dir, len(fileInfos))

	return fileInfos, nil
}

// 递归查询子目录，指定查询深度
func QueryFilesRecursive(dir string, option *model.QueryOption) ([]*model.FileInfo, error) {
	if option == nil {
		option = model.GetDefaultOption()
	}

	if option.GetDepth() == 0 {
		return QueryFiles(dir, option)
	}

	curQueue := util.NewDeque()
	nextQueue := util.NewDeque()
	curQueue.AddLast(dir)
	res := make([]*model.FileInfo, 0)

	for i := 0; i <= option.GetDepth(); i++ {
		for curQueue.Size() > 0 {
			curDir := curQueue.RemoveFirst().(string)
			tmp, err := QueryFiles(curDir, option)
			if err != nil {
				return res, err
			}

			for _, fileInfo := range tmp {
				if fileInfo == nil {
					continue
				}
				if fileInfo.IsDir() && i < option.GetDepth() {
					nextQueue.AddLast(fileInfo.GetPath())
				}
				if option.Filter(fileInfo) {
					res = append(res, fileInfo)
				}
			}
		}
		curQueue, nextQueue = nextQueue, curQueue
	}

	log.Debug("query files recursive success, dir=%v, fileNum=%v", dir, len(res))

	return res, nil
}

// 获取windows驱动列表
func getWindowsDrivers() ([]*model.FileInfo, error) {
	partitions, err := disk.Partitions(false)
	if err != nil {
		log.Error("get drivers failed, err=%v", err)
		return nil, err
	}
	drivers := make([]*model.FileInfo, len(partitions))
	for i, partition := range partitions {
		drivers[i] = &model.FileInfo{
			Name: partition.Mountpoint,
			Type: consts.FileTypeDriver,
		}
	}

	log.Debug("query drivers success, driverNum=%v", len(drivers))

	return drivers, nil
}

func readDir(dir string) ([]*model.FileInfo, error) {
	dirEntries, err := os.ReadDir(dir)
	if err != nil {
		return nil, err
	}

	fileInfos := make([]*model.FileInfo, len(dirEntries))
	for i, dirEntry := range dirEntries {
		fileInfos[i] = model.BuildFileInfo(dir, dirEntry)
	}

	return fileInfos, nil
}
